home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / xvisrc.zip / SEARCH.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  27KB  |  1,234 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)search.c    2.1 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     search.c
  14. * module function:
  15.     Regular expression searching, including global command.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25. #include "regexp.h"    /* Henry Spencer's regular expression routines */
  26. #include "regmagic.h"    /* Henry Spencer's regular expression routines */
  27.  
  28. #ifdef    MEGAMAX
  29. overlay "search"
  30. #endif
  31.  
  32. /*
  33.  * String searches
  34.  *
  35.  * The actual searches are done using Henry Spencer's regular expression
  36.  * library.
  37.  */
  38.  
  39. /*
  40.  * Names of values for the P_regextype enumerated parameter.
  41.  */
  42. char    *rt_strings[] =
  43. {
  44.     "tags",
  45.     "grep",
  46.     "egrep",
  47.     NULL
  48. };
  49.  
  50. /*
  51.  * Used by g/re/p to remember where we are and what we are doing.
  52.  */
  53. static    Line    *curline;
  54. static    Line    *lastline;
  55. static    long    curnum;
  56. static    bool_t    greptype;
  57.  
  58. static    Posn    *bcksearch P((Xviwin *, Line *, int, bool_t));
  59. static    Posn    *fwdsearch P((Xviwin *, Line *, int, bool_t));
  60. static    char    *mapstring P((char **, int));
  61. static    char    *compile P((char *, int, bool_t));
  62. static    char    *grep_line P((void));
  63. static    long    substitute P((Xviwin *, Line *, Line *, char *, char *));
  64.  
  65. /*
  66.  * Convert a regular expression to egrep syntax: the source string can
  67.  * be either tags compatible (only ^ and $ are significant), vi
  68.  * compatible or egrep compatible (but also using \< and \>)
  69.  *
  70.  * Our first parameter here is the address of a pointer, which we
  71.  * point to the closing delimiter character if we found one, otherwise
  72.  * the closing '\0'.
  73.  */
  74. static char *
  75. mapstring(sp, delim)
  76. char    **sp;        /* pointer to pointer to pattern string */
  77. int    delim;        /* delimiter character */
  78. {
  79.     static Flexbuf    ns;
  80.     int            rxtype;    /* can be rt_TAGS, rt_GREP or rt_EGREP */
  81.     register enum {
  82.     m_normal,    /* nothing special */
  83.     m_startccl,    /* just after [ */
  84.     m_negccl,    /* just after [^ */
  85.     m_ccl,        /* between [... or [^... and ] */
  86.     m_escape    /* just after \ */
  87.     }    state = m_normal;
  88.     register char    *s;
  89.  
  90.     rxtype = Pn(P_regextype);
  91.  
  92.     flexclear(&ns);
  93.     for (s = *sp; *s != '\0' && (*s != delim || state != m_normal); s++) {
  94.     switch (state) {
  95.     case m_normal:
  96.         switch (*s) {
  97.         case '\\':
  98.         state = m_escape;
  99.         break;
  100.  
  101.         case '(': case ')': case '+': case '?': case '|':
  102.         /* egrep metacharacters */
  103.         if (rxtype != rt_EGREP)
  104.             (void) flexaddch(&ns, '\\');
  105.         (void) flexaddch(&ns, *s);
  106.         break;
  107.  
  108.         case '*': case '.': case '[':
  109.         /* grep metacharacters */
  110.         if (rxtype == rt_TAGS) {
  111.             (void) flexaddch(&ns, '\\');
  112.         } else if (*s == '[') {
  113.             /* start of character class */
  114.             state = m_startccl;
  115.         }
  116.          /* fall through ... */
  117.  
  118.         default:
  119.         (void) flexaddch(&ns, *s);
  120.         }
  121.         break;
  122.  
  123.     case m_startccl:
  124.     case m_negccl:
  125.         (void) flexaddch(&ns, *s);
  126.         state = (*s == '^' && state == m_startccl) ? m_negccl : m_ccl;
  127.         break;
  128.  
  129.     case m_ccl:
  130.         (void) flexaddch(&ns, *s);
  131.         if (*s == ']')
  132.         state = m_normal;
  133.         break;
  134.  
  135.     case m_escape:
  136.         switch (*s) {
  137.         case '(':        /* bracket conversion */
  138.         case ')':
  139.         if (rxtype != rt_GREP)
  140.             (void) flexaddch(&ns, '\\');
  141.         (void) flexaddch(&ns, *s);
  142.         break;
  143.  
  144.         case '.':        /* egrep metacharacters */
  145.         case '\\':
  146.         case '[':
  147.         case '*':
  148.         case '?':
  149.         case '+':
  150.         case '^':
  151.         case '$':
  152.         case '|':
  153.         (void) lformat(&ns, "\\%c", *s);
  154.         break;
  155.  
  156.         default:        /* a normal character */
  157.         if (*s != delim)
  158.             (void) flexaddch(&ns, '\\');
  159.         (void) flexaddch(&ns, *s);
  160.         }
  161.         state = m_normal;
  162.     }
  163.     }
  164.  
  165.     *sp = s;
  166.  
  167.     /*
  168.      * This is horrible, but the real vi does it, so ...
  169.      */
  170.     if (state == m_escape) {
  171.     (void) lformat(&ns, "\\\\");
  172.     }
  173.     return flexgetstr(&ns);
  174. }
  175.  
  176. /**********************************************************
  177.  *                              *
  178.  * Abstract type definition.                  *
  179.  *                              *
  180.  * Regular expression node, with pointer reference count. *
  181.  *                              *
  182.  * We need this for global substitute commands.          *
  183.  *                              *
  184.  **********************************************************/
  185.  
  186. typedef struct {
  187.     regexp    *rn_ptr;
  188.     int        rn_count;
  189. } Rnode;
  190.  
  191. /*
  192.  * Node for last successfully compiled regular expression.
  193.  */
  194. static Rnode    *lastprogp = NULL;
  195.  
  196. /*
  197.  * Last regular expression used in a substitution.
  198.  */
  199. static    Rnode    *last_lhs = NULL;
  200.  
  201. /*
  202.  * Last rhs for a substitution.
  203.  */
  204. static    char    *last_rhs = NULL;
  205.  
  206. /*
  207.  * rn_new(), rn_delete() & rn_duplicate() perform operations on Rnodes
  208.  * which are respectively analogous to open(), close() & dup() for
  209.  * Unix file descriptors.
  210.  */
  211.  
  212. /*
  213.  * Make a new Rnode, given a pattern string.
  214.  */
  215. static Rnode *
  216. rn_new(str)
  217.     char    *str;
  218. {
  219.     Rnode    *retp;
  220.  
  221.     if ((retp = (Rnode *) alloc(sizeof (Rnode))) == NULL)
  222.     return NULL;
  223.     if ((retp->rn_ptr = regcomp(str)) == NULL) {
  224.     free ((char *) retp);
  225.     return NULL;
  226.     }
  227.     retp->rn_count = 1;
  228.     return retp;
  229. }
  230.  
  231. /*
  232.  * Make a copy of an Rnode pointer & increment the Rnode's reference
  233.  * count.
  234.  */
  235. #define rn_duplicate(s)    ((s) ? ((s)->rn_count++, (s)) : NULL)
  236.  
  237. /*
  238.  * Decrement an Rnode's reference count, freeing it if there are no
  239.  * more pointers pointing to it.
  240.  *
  241.  * In C++, this would be a destructor for an Rnode.
  242.  */
  243. static void
  244. rn_delete(rp)
  245. Rnode    *rp;
  246. {
  247.     if (rp != NULL && --rp->rn_count <= 0) {
  248.     free((char *) rp->rn_ptr);
  249.     free((char *) rp);
  250.     }
  251. }
  252.  
  253. #if 0
  254. /*
  255.  * Increment the reference count for the current prog,
  256.  * and return it to the caller.
  257.  */
  258. static Rnode *
  259. inccount()
  260. {
  261.     if (lastprogp != NULL) {
  262.     lastprogp->rn_count++;
  263.     }
  264.     return(lastprogp);
  265. }
  266.  
  267. #endif
  268.  
  269. #define    cur_prog()    (lastprogp->rn_ptr)
  270.  
  271. /*
  272.  * Compile given regular expression from string.
  273.  *
  274.  * The opening delimiter for the regular expression is supplied; the
  275.  * end of it is marked by an unescaped matching delimiter or, if
  276.  * delim_only is FALSE, by a '\0' character. We return a pointer to
  277.  * the terminating '\0' or to the character following the closing
  278.  * delimiter, or NULL if we failed.
  279.  *
  280.  * If, after we've found a delimiter, we have an empty pattern string,
  281.  * we use the last compiled expression if there is one.
  282.  *
  283.  * The regular expression is converted to egrep syntax by mapstring(),
  284.  * which also finds the closing delimiter. The actual compilation is
  285.  * done by regcomp(), from Henry Spencer's regexp routines.
  286.  *
  287.  * If we're successful, the compiled regular expression will be
  288.  * pointed to by lastprogp->rn_ptr, & lastprogp->rn_count will be > 0.
  289.  */
  290. static char *
  291. compile(pat, delimiter, delim_only)
  292. char    *pat;
  293. int    delimiter;
  294. bool_t    delim_only;
  295. {
  296.     Rnode    *progp;
  297.  
  298.     if (pat == NULL) {
  299.     return(NULL);
  300.     }
  301.  
  302.     /*
  303.      * If we get an empty regular expression, we just use the last
  304.      * one we compiled (if there was one).
  305.      */
  306.     if (*pat == '\0') {
  307.     return((delim_only || lastprogp == NULL) ? NULL : pat);
  308.     }
  309.     if (*pat == delimiter) {
  310.     return((lastprogp == NULL) ? NULL : &pat[1]);
  311.     }
  312.  
  313.     progp = rn_new(mapstring(&pat, delimiter));
  314.     if (progp == NULL) {
  315.     return(NULL);
  316.     }
  317.  
  318.     if (*pat == '\0') {
  319.     if (delim_only) {
  320.         rn_delete(progp);
  321.         return(NULL);
  322.     }
  323.     } else {
  324.     pat++;
  325.     }
  326.     rn_delete(lastprogp);
  327.     lastprogp = progp;
  328.     return(pat);
  329. }
  330.  
  331. Posn *
  332. search(window, startline, startindex, dir, strp)
  333. Xviwin        *window;
  334. Line        *startline;
  335. int        startindex;
  336. int        dir;        /* FORWARD or BACKWARD */
  337. char        **strp;
  338. {
  339.     Posn    *pos;
  340.     Posn    *(*sfunc) P((Xviwin *, Line *, int, bool_t));
  341.     char    *str;
  342.  
  343.     str = compile(*strp, (dir == FORWARD) ? '/' : '?', FALSE);
  344.     if (str == NULL) {
  345.     return(NULL);
  346.     }
  347.     *strp = str;
  348.  
  349.     if (dir == BACKWARD) {
  350.     sfunc = bcksearch;
  351.     } else {
  352.     sfunc = fwdsearch;
  353.     }
  354.     pos = (*sfunc)(window, startline, startindex, Pb(P_wrapscan));
  355.  
  356.     return(pos);
  357. }
  358.  
  359. /*
  360.  * Search for the given expression, ignoring regextype, without
  361.  * wrapscan & and without using the compiled regular expression for
  362.  * anything else (so 'n', 'N', etc., aren't affected). We